let express = require("express");
let bodyParser = require("body-parser");
let session = require("express-session");
let OAuthServer = require("oauth2-server");
let UUID = require("uuid");
let util = require("util");

var redis = require("redis");
var client = redis.createClient({
    "host": "localhost",
    "port": 6380,
    "password": "123456"
});

let app = express();

app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

//app.set("trust proxy", 1); // trust first proxy
app.use(session({
    key: "connect.sid",
    secret: "oauth-server demo",
    resave: false,
    saveUninitialized: false,
    cookie: {secure: false, maxAge: 86400 * 1e3},
    store: (new require("express-sessions"))({
        storage: "redis",
        instance: client,
        host: "localhost",
        port: 6380,
        password: "123456",
        collection: "sessions",
        expire: 86400
    })
}));

app.use(express.static("public"));

app.set("views", "./views");
app.set("view engine", "ejs");

//let model = require("./model/oauth2/dynamodb/model");
let model = require("./model/oauth2/memory/model");
//let model = require("./model/oauth2/redis/model");
//let model = require("./model/oauth2/mongodb/model");
//let model = require("./model/oauth2/postgresql/model");

app.oauth = new OAuthServer({
    model: model,
    grants: ["password", "refresh_token", "authorization_code"],
    debug: true
});


setInterval(function () { model.dump(); }, 10000);


/**
 * Auto Test Authorization Token Grant Type, 自动化测试Authorization Token类型授权
 *
 * 1. Set model redirectUri = /oauth/token/test_authorization_code First
 *
 * 2. Then Request
 *
 * http://localhost:3000/oauth/authorise?client_id=plane&redirect_uri=/oauth/token/test_authorization_code
 *
 * 3. It Will Auto Jump To
 *
 * http://localhost:3000/login?redirect=/oauth/authorise&client_id=plane&redirect_uri=/oauth/token/test_authorization_code
 *
 * 4. Input auth information, Then Click yes
 *
 * It Will Return
 *
 * {"token_type":"bearer","access_token":"bfa84d648ec896caa095149eba1ed0aebdd70bea","expires_in":3600,"refresh_token":"74931817012b3fed08367191618e339d90f45eff"}
 */
app.get("/oauth/token/test_authorization_code", function (req, res, next) {
    console.log("Get /oauth/token/test_authorization_code");
    req.body = req.body ? req.body : {};

    req.method = "POST";

    req.headers["content-type"] = "application/x-www-form-urlencoded";
    req.headers["content-length"] = JSON.stringify(req.body).length;
    req.headers["transfer-encoding"] = "utf8";

    req.body.client_id = req.query.client_id || "plane";
    req.body.client_secret = req.query.client_secret || "THISisAUTO-generatedSECRET";
    req.body.grant_type = req.query.grant_type || "authorization_code";
    req.body.code = req.query.code || "acd8df5fbb2d856fb6dc0896171a52aa24eca299";

    for (let key in req.query) {
        if (req.query.hasOwnProperty(key)) {
            req.body[key] = req.query[key];
        }
    }
    next();
}, app.oauth.grant());

/**
 * Auto Test Refresh Token Grant Type, 自动化测试Refresh Token类型授权
 *
 * 1. Set model redirectUri = /oauth/token/test_password First
 *
 * 2. Then Request
 *
 * http://localhost:3000/oauth/authorise?client_id=plane&redirect_uri=/oauth/token/test_password
 *
 * 3. It Will Auto Jump To
 *
 * http://localhost:3000/login?redirect=/oauth/authorise&client_id=plane&redirect_uri=/oauth/token/test_password
 *
 * 4. Input auth information, Then Click yes
 *
 * It Will Return
 *
 * {"token_type":"bearer","access_token":"bfa84d648ec896caa095149eba1ed0aebdd70bea","expires_in":3600,"refresh_token":"74931817012b3fed08367191618e339d90f45eff"}
 */
app.get("/oauth/token/test_password", function (req, res, next) {
    console.log("Get /oauth/token/test_password");
    req.body = req.body ? req.body : {};
    for (let key in req.query) {
        if (req.query.hasOwnProperty(key)) {
            req.body[key] = req.query[key];
        }
    }

    req.method = "POST";

    req.headers["content-type"] = "application/x-www-form-urlencoded";
    req.headers["content-length"] = JSON.stringify(req.body).length;
    req.headers["transfer-encoding"] = "utf8";

    req.body.client_id = "plane";
    req.body.client_secret = "THISisAUTO-generatedSECRET";
    req.body.grant_type = "password";
    req.body.username = "justin";
    req.body.password = "123456";
    next();
}, app.oauth.grant());

/**
 * Auto Test Refresh Token Grant Type, 自动化测试Refresh Token类型授权
 *
 * refresh_token 是在 password 或 authorization_code 授权后产生的时间比较持久的token, 用于刷新原来的 access_token
 *
 * ** 暂时无法直接使用此方法来做检测 **
 *
 * 1. Set model redirectUri = /oauth/token/test_refresh_token First
 *
 * 2. Then Request
 *
 * http://localhost:3000/oauth/authorise?client_id=plane&redirect_uri=/oauth/token/test_refresh_token
 *
 * 3. It Will Auto Jump To
 *
 * http://localhost:3000/login?redirect=/oauth/authorise&client_id=plane&redirect_uri=/oauth/token/test_refresh_token
 *
 * 4. Input auth information, Then Click yes
 *
 * It Will Return
 *
 * {"token_type":"bearer","access_token":"bfa84d648ec896caa095149eba1ed0aebdd70bea","expires_in":3600,"refresh_token":"74931817012b3fed08367191618e339d90f45eff"}
 */
app.get("/oauth/token/test_refresh_token", function (req, res, next) {
    console.log("Get /oauth/token/test_refresh_token");
    req.body = req.body ? req.body : {};

    req.method = "POST";

    req.headers["content-type"] = "application/x-www-form-urlencoded";
    req.headers["content-length"] = JSON.stringify(req.body).length;
    req.headers["transfer-encoding"] = "utf8";

    req.body.client_id = req.query.client_id || "plane";
    req.body.client_secret = req.query.client_secret || "THISisAUTO-generatedSECRET";
    req.body.grant_type = req.query.grant_type || "refresh_token";
    req.body.refresh_token = req.query.refresh_token || "de429ea1e9b442233a3b3ad001707d3572ba6079";

    for (let key in req.query) {
        if (req.query.hasOwnProperty(key)) {
            req.body[key] = req.query[key];
        }
    }
    next();
}, app.oauth.grant());

/**
 * 测试时, 将回调地址写成此地址, 用以回调时调用测试用例, 执行测试代码
 */
app.get("/getSecret", function (req, res) {
    console.log("Ready To Get Secret");
    let token = req.query.code;
    let type = req.query.type || "authorization_code";
    let getSecret;

    switch (type) {
        case "password":
            getSecret = require("./test/password");
            token = token || "";                                            // No Need To Set Token Code
            break;
        case "refresh_token":
            getSecret = require("./test/refresh_token");
            token = token || "de429ea1e9b442233a3b3ad001707d3572ba6079";    // refresh_token
            break;
        case "authorization_code":
        default:
            getSecret = require("./test/authorization_code");
            token = token || "acd8df5fbb2d856fb6dc0896171a52aa24eca299";    // auth_token
            break;
    }

    getSecret.loadData(token, function (err, data) {
        if (!err) res.end(data);
        else res.end("Invalid Data!");
    });
});


/**
 * Handle token grant requests, 获取Access Token的接口, 获取方式有4种, 目前测试3种, 测试方案如上方
 */
app.post("/oauth/token", app.oauth.grant());


// Show them the "do you authorise xyz app to access your content?" page
app.get("/oauth/authorise", function (req, res) {
    console.log("Get /oauth/authorise [ %j ] [ %j ] [ %j ]", req.body, req.query, req.session);
    if (!req.session || !req.session.user) {
        // If they aren"t logged in, send them to your own login implementation
        return res.redirect(util.format("/login?redirect=%s&client_id=%s&redirect_uri=%s", req.path, req.query.client_id || "", req.query.redirect_uri || ""));
    }

    res.render("authorise", {
        client_id: req.query.client_id,
        redirect_uri: req.query.redirect_uri
    });
});
// Handle authorise
app.post("/oauth/authorise", function (req, res, next) {
    console.log("Post /oauth/authorise [ %j ] [ %j ] [ %j ]", req.body, req.query, req.session);

    if (!req.session.user) {
        return res.redirect(util.format("/login?client_id=%s&redirect_uri=%s", req.body.client_id || "", req.body.redirect_uri || ""));
    }

    next();
}, app.oauth.authCodeGrant(function (req, next) {
    // The first param should to indicate an error
    // The second param should a bool to indicate if the user did authorise the app
    // The third param should for the user/uid (only used for passing to saveAuthCode)
    next(null, req.body.allow === "yes", req.session.user, req.session.user);
}));


// Show login
app.get("/login", function (req, res) {
    console.log("Get /login [ %j ] [ %j ] [ %j ]", req.body, req.query, req.session);
    res.render("login", {
        redirect: req.query.redirect || req.body.redirect,
        client_id: req.query.client_id || req.body.client_id,
        redirect_uri: req.query.redirect_uri || req.body.redirect_uri
    });
});
// Handle login
app.post("/login", function (req, res) {
    // Insert your own login mechanism
    console.log("Post /login [ %j ] [ %j ] [ %j ]", req.body, req.query, req.session);
    if (req.body.email !== "123") {
        return res.redirect(util.format("/login?redirect=%s&client_id=%s&redirect_uri=%s", req.body.redirect || "", req.body.client_id || "", req.body.redirect_uri || ""));
    } else {
        // Successful logins should send the user back to the /oauth/authorise
        // with the client_id and redirect_uri (you could store these in the session)
        if (!req.session || !req.session.user) {
            req.session.user = {id: UUID.v4()};
            req.session.save(function () {
                console.log("Session Saved To DB [ %j ]", req.session);
            });
        }
        console.log("Create Session [ %j ]", req.session);
        return res.redirect(util.format("%s?client_id=%s&redirect_uri=%s", req.body.redirect || "/home", req.body.client_id || "", req.body.redirect_uri || ""));
    }
});


app.get("/register", function (req, res) {
    console.log("Get /login [ %j ] [ %j ] [ %j ]", req.body, req.query, req.session);
    res.render("register", {
        redirect: req.query.redirect || req.body.redirect,
        client_id: req.query.client_id || req.body.client_id,
        redirect_uri: req.query.redirect_uri || req.body.redirect_uri
    });
});


/**
 * 默认回调地址, 在login成功后, 没有设置redirect的时候跳转到此
 */
app.get("/home", function (req, res) {
    res.send("Success");
});


/**
 * 私密接口, 请求方法:
 *
 * Get /secret?access_token=bfa84d648ec896caa095149eba1ed0aebdd70bea
 *
 * Or
 *
 * Post {"form": {"access_token": "bfa84d648ec896caa095149eba1ed0aebdd70bea"}}
 *
 * It Will Return "Secret area"
 */
app.all("/secret", app.oauth.authorise(), function (req, res) {
    // Will require a valid access_token
    res.send("Secret area");
});

/**
 * 公开接口, 请求方法:
 *
 * Get /public
 *
 * Or
 *
 * Post {"form":{}}
 *
 * It Will Return "Public area"
 */
app.all("/public", function (req, res) {
    // Does not require an access_token
    res.send("Public area");
});

/**
 * Error handling 错误处理函数
 */
app.use(app.oauth.errorHandler());

app.listen(3000, function () {
    console.log("Sever Listen On Port [ %s ]", 3000);
});


/**
 * Model 接口实现
 *
 * ## 必须方法
 *
 * getAccessToken (bearerToken, callback)
 * saveAccessToken (accessToken, clientId, expires, user, callback)
 * getClient (clientId, clientSecret, callback)
 * grantTypeAllowed (clientId, grantType, callback)
 *
 * ## 可选方法
 *
 * generateToken (type, req, callback)
 *
 *
 *
 * ### authorization_code 方式
 *
 * getAuthCode (authCode, callback)
 * saveAuthCode (authCode, clientId, expires, user, callback)
 *
 * ### password 方式
 *
 * getUser (username, password, callback)
 *
 * ### refresh_token 方式
 *
 * saveRefreshToken (refreshToken, clientId, expires, user, callback)
 * getRefreshToken (refreshToken, callback)
 *
 * revokeRefreshToken (refreshToken, callback)
 *
 * ### extension grant 方式
 *
 * extendedGrant (grantType, req, callback)
 *
 * ### client_credentials 方式
 *
 * getUserFromClient (clientId, clientSecret, callback)
 *
 */
